#pragma once
#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>

using namespace std;
// 阻塞队列的特点:
// 1. 当队列为空时,从队列中获取数据的操作被阻塞,直到队列中放入数据;
// 2. 当队列满时,向队列中存放数据的操作被阻塞,直到队列中取出数据;

template <class T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _block_queue.size() == _capacity;
    }
    bool IsEmpty()
    {
        return _block_queue.empty();
    }
public:
    BlockQueue(int capacity)
        : _capacity(capacity)
    {
        pthread_mutex_init(&(_mutex), nullptr);
        pthread_cond_init(&(_product_cond), nullptr);
        pthread_cond_init(&(_consume_cond), nullptr);
    }
    void EnQueue(const T &in) // 生产者将数据放入队列
    {
        // 1. 生产者向队列中存放数据时,存在消费者从队列中获取数据的可能---加锁保护(保护生产过程安全)
        pthread_mutex_lock(&(_mutex));
        // 2. 检测队列是否满足生产条件,若队列已满,则进行阻塞等待
        if (IsFull())
        {
            //3. 生产线程进入指定的条件变量下的等待队列&&释放曾经持有的锁
            pthread_cond_wait(&(_product_cond),&(_mutex));
        }
        //4. 生产数据
        //_block_queue.push(std::move(in));
        _block_queue.push(in);
        //5.最初阶段阻塞队列为空,消费线程在队列中等待,生产线程生产数据,当生产到一定阶段就应该将等待队列中的消费线程唤醒
        pthread_cond_signal(&(_consume_cond));


        pthread_mutex_unlock(&(_mutex));
    }
    
    //参数设计为指针,输出型参数,将获取到的数据传递出去
    void DeQueue(T *out) // 消费者从队列中获取数据
    {
        // 1. 消费者从队列中获取数据时,存在生产者从队列中存放数据的可能---加锁保护(保护消费过程安全)
        pthread_mutex_lock(&(_mutex));
        // 2. 检测队列是否满足消费条件,若队列已空,则进行阻塞等待
        if(IsEmpty())
        {
            //3. 消费线程进入指定的条件变量下的等待队列&&释放曾经持有的锁
            pthread_cond_wait(&(_consume_cond),&(_mutex));
        }
       //4. 获取数据
        *out=_block_queue.front();
        _block_queue.pop();
       //5.当消费线程将阻塞队列中的数据获取,当消费到一定阶段就应该将等待队列中的生产线程唤醒进行生产
       pthread_cond_signal(&(_product_cond));

        pthread_mutex_unlock(&(_mutex));
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&(_mutex));
        pthread_cond_destroy(&(_product_cond));
        pthread_cond_destroy(&(_consume_cond));
    }

private:
    queue<T> _block_queue; //_block_queue承装数据的容器
    int _capacity;         // 容器的容量(总上限)

    // 阻塞队列为共享资源,需要互斥锁保证资源安全
    pthread_mutex_t _mutex; // 保护_block_queue的互斥锁

    // 生产者将阻塞队列放满时,向队列中存放数据的操作应该被阻塞---条件变量1
    pthread_cond_t _product_cond; // 专门给生产者提供的条件变量

    // 消费者将阻塞队列取空时,从队列中获取数据的操作应该被阻塞----条件变量2
    pthread_cond_t _consume_cond; // 专门给消费者提供的条件变量
    // 定义两个条件变量是方便以后的唤醒操作
};